using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Nop.Services.Catalog;
using Nop.Core;
using Nop.Services.Media;
using Nop.Core.ApiDataModel;
using Nop.Core.Domain.Media;
using Spotshub.Api.Filter;
using WebGrease.Css.Extensions;
using System.Runtime.Caching;

namespace Spotshub.Api.Controllers.V1
{
    [ExceptionHandler]
    public class CategoryController : ApiController
    {
		private const string CATEGORY_ALL_KEY = "category_all_key";
		private const string CATEGORY_BY_ID_KEY = "category_id_{0}";
		private const string CATEGORY_ON_HOME_KEY = "category_on_home";
		private const string SLIDESHOW_ON_HOME = "slideshow_on_home";

        ICategoryService _categoryService;
		IPictureService _pictureService;
		IPictureDisplayService _pictureDisplayService;
		MemoryCache _cache;
		MediaSettings _mediaSettings;
		
        public CategoryController(ICategoryService categoryService, IPictureService pictureService,
			IPictureDisplayService pictureDisplayService, MemoryCache cache, MediaSettings mediaSettings)
        {
            this._categoryService = categoryService;
			this._pictureService = pictureService;
			this._pictureDisplayService = pictureDisplayService;
			this._cache = cache;
			this._mediaSettings = mediaSettings;
        }

        // GET: api/Category
        public IEnumerable<Nop.Core.ApiDataModel.Category> Get()
        {			
			if (_cache.Contains(CATEGORY_ALL_KEY))
			{
				return _cache.Get(CATEGORY_ALL_KEY) as IEnumerable<Nop.Core.ApiDataModel.Category>;
			}
			var result = GetAllCategoryTree(false).OrderBy(c => c.DisplayOrder);
			_cache.Add(CATEGORY_ALL_KEY, result, new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20)) });
			return result;
        }

        // GET: api/Category/5
        public Nop.Core.ApiDataModel.Category Get(int id)
        {
			var key = string.Format(CATEGORY_BY_ID_KEY, id);
			if (_cache.Contains(key))
			{
				return _cache.Get(key) as Nop.Core.ApiDataModel.Category;
			}
			else
			{
				Nop.Core.ApiDataModel.Category result = null;
				List<Nop.Core.ApiDataModel.Category> allCategoryDataModel = GetAllCategoryTree(false);
				foreach (var category in allCategoryDataModel)
				{
					result = FindCategory(category, id);
					if (result != null)
						break;
				}
				if (result != null)
				{
					_cache.Add(key, result, 
						new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20)) });
				}
				return result;
			}
			
        }

        // GET: api/category/GetAllCategoriesDisplayedOnHomePage/
        public IEnumerable<Nop.Core.ApiDataModel.Category> GetAllCategoriesDisplayedOnHomePage()
        {
            //return (IEnumerable<Nop.Core.ApiDataModel.Category>)_cache.AddOrGetExisting(CATEGORY_ON_HOME_KEY,
            //    GetAllCategoryTree(true).OrderBy(c => c.DisplayOrder),
            //    new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20)) });
            var getMessage = _cache.AddOrGetExisting(CATEGORY_ON_HOME_KEY,
                GetAllCategoryTree(true).OrderBy(c => c.DisplayOrder),
                new CacheItemPolicy {AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(20))});
            if (getMessage == null)
            {
                return GetAllCategoryTree(true).OrderBy(c => c.DisplayOrder);
            }
            return (IEnumerable<Nop.Core.ApiDataModel.Category>) getMessage;
        }

		// Get: api/category/getHomePageSlideShow
		public object GetHomePageSlideShow(bool useCache=true)
		{
			if (useCache)
			{
				if (_cache.Contains(SLIDESHOW_ON_HOME))
					return _cache.Get(SLIDESHOW_ON_HOME);
			}

			object result = null;
			var slideShow = this._pictureDisplayService.GetSlideshowBySystemName(_mediaSettings.HomePageSlideShow);
			if (slideShow == null)
				result = new { Success = false, Items = new SlideShowModel[]{} };
			var slideShowModel = new SlideShowModel(slideShow);
			if (slideShowModel.PictureLinkCollection != null && slideShowModel.PictureLinkCollection.PictureLinks != null)
			{
                slideShowModel.PictureLinkCollection.PictureLinks.ToList().ForEach(pl =>
				{
					if (pl.PictureId > 0)
					{
						pl.PictureUrl = GetPictureUrl(pl.PictureId);
					}
				});
			    
			}   
		    result = new { Success = true, Items = new SlideShowModel[] { slideShowModel } };
			_cache.Add(SLIDESHOW_ON_HOME, result, new CacheItemPolicy { AbsoluteExpiration = new DateTimeOffset(DateTime.Now.AddMinutes(10)) });
			return result;
		}

		[NonAction]
		private Nop.Core.ApiDataModel.Category FindCategory(Nop.Core.ApiDataModel.Category category, int catId)															
		{
			if (category.CategoryId == catId) return category;
			if (category.SubCategories.Any())
			{ 
				foreach(var subCategory in category.SubCategories)
				{
					var result = FindCategory(subCategory, catId);
					if (result != null) 
						return result;
				}
			}
			return null;
		}

        /// <summary>
        /// Get all the categories as a parent/child tree hierarchy.
        /// </summary>
        /// <returns> List of root categories, each root category may have some child categories. </returns>
        private List<Nop.Core.ApiDataModel.Category> GetAllCategoryTree(bool showOnHomePageOnly = false)
        {
            List<Nop.Core.ApiDataModel.Category> allCategoryFromDb = GetAllCategoriesFlat(showOnHomePageOnly);
            List<Nop.Core.ApiDataModel.Category> allCategoryDataModel = new List<Nop.Core.ApiDataModel.Category>();
            Queue<Nop.Core.ApiDataModel.Category> currentParentCategory = new Queue<Nop.Core.ApiDataModel.Category>();

            for(int i = allCategoryFromDb.Count() - 1; i >= 0; i--)
            {
                if (allCategoryFromDb[i].ParentCategoryId==0)
                {
                    allCategoryDataModel.Add(allCategoryFromDb[i]);
                    allCategoryFromDb.RemoveAt(i);
                }
            }
            allCategoryDataModel.ForEach(category => currentParentCategory.Enqueue(category));
			allCategoryFromDb.ForEach(category => currentParentCategory.Enqueue(category));
            
            while(currentParentCategory.Count() > 0)
            {
                Nop.Core.ApiDataModel.Category parentCategory = currentParentCategory.Dequeue();
				for (int i = 0; i < allCategoryFromDb.Count(); i++)
                {
                    if (allCategoryFromDb[i].ParentCategoryId == parentCategory.CategoryId)
                    {
                        parentCategory.SubCategories.Add(allCategoryFromDb[i]);                        
                    }
                }
            }

            return allCategoryDataModel;
        }

        private List<Nop.Core.ApiDataModel.Category> GetAllCategoriesFlat(bool showOnHomePageOnly = false)
        {
            List<Nop.Core.Domain.Catalog.Category> allCategoryFromDb;

            if (showOnHomePageOnly)
            {
                allCategoryFromDb = this._categoryService.GetAllCategoriesDisplayedOnHomePage().ToList();
            }
            else
            {
                allCategoryFromDb = this._categoryService.GetAllCategories().ToList();
            }
            List<Nop.Core.ApiDataModel.Category> allCategoryDataModel = new List<Nop.Core.ApiDataModel.Category>();
			allCategoryFromDb.ForEach(category => allCategoryDataModel.Add(
				new Nop.Core.ApiDataModel.Category(category) { PictureUrl = GetPictureUrl(category.PictureId) }));
			allCategoryDataModel.ForEach(cm =>
			{ 
				if(cm.SlideShow!=null && cm.SlideShow.PictureLinkCollection!=null && cm.SlideShow.PictureLinkCollection.PictureLinks!=null) 
					cm.SlideShow.PictureLinkCollection.PictureLinks.ToList().ForEach(pl =>
					{
						if ( pl.PictureId > 0)
						{
							pl.PictureUrl = GetPictureUrl(pl.PictureId);
						}
					});
			});

            return allCategoryDataModel;
        }

		private string GetPictureUrl(int pictureId)
		{
			return _pictureService.GetPictureUrl(pictureId);
		}
    }
}
